package com.selen.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author Selen
 */
public class JWTUtil {

    /** token令牌服务端秘钥，值随意，但千万不要泄露 */
    private static final String SECRET_KEY = "my-secret";
    private static final Long EXPIRATION_TIME_THRESHOLD = 1000 * 60 * 5L;
    private static final String EXPIRING_SOON = "expiringSoon";

    /**
     * 生成 token
     * @param id 用户id
     * @param nickName 用户姓名
     * @param avatar 用户头像
     * @return token 字符串
     */
    public static String build(Long id, String nickName, String avatar){

        if (ObjectUtil.hasNull(id,nickName,avatar)){
            throw new RuntimeException("JWT 负载含空");
        }
        JWTCreator.Builder builder = JWT.create();
        builder.withClaim("id", id);
        builder.withClaim("nickName", nickName);
        builder.withClaim("avatar", avatar);
        // 保证 Token 的唯一性
        builder.withClaim("created", System.currentTimeMillis());
        builder.withSubject("用户登录认证");
        builder.withIssuer("selen");
        builder.withIssuedAt(new Date());
        builder.withExpiresAt(new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 24L)));

        String token = builder.sign(Algorithm.HMAC256(SECRET_KEY));
        return token;
    }

    /**
     * 解码 token
     * @param token token 字符串
     * @return 用户信息
     */
    public static Map<String, Object> verify(String token){
        Map<String, Object> result = new HashMap<>(4);
        if (StrUtil.isEmpty(token)) {
            throw new RuntimeException("token令牌为空");
        }
        // 通过秘钥验证token字符串，验证失败直接抛出异常
        DecodedJWT decodedJwt = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build().verify(token);

        Long id = decodedJwt.getClaim("id").asLong();
        String name = decodedJwt.getClaim("name").asString();
        String avatar = decodedJwt.getClaim("avatar").asString();

        // 判断Token是否即将过期
        if (decodedJwt.getExpiresAt().getTime() - System.currentTimeMillis()
                < EXPIRATION_TIME_THRESHOLD) {
            result.put(EXPIRING_SOON, true);
            result.put("newToken", build(id, name, avatar));
        } else {
            result.put("id", id);
            result.put("name", name);
            result.put("avatar", avatar);
            result.put(EXPIRING_SOON, false);
        }

        return result;
    }

    /**
     * 验证 token ，失败抛出异常，成功返回id
     * @param token token字符串
     * @return 用户id
     */
    public static Long getId(String token){
        return (Long)verify(token).get("id");
    }


}
